<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>Transient errors</title>

 </head>
 <body><div class="manualnavbar" style="text-align: center;">
 <div class="prev" style="text-align: left; float: left;"><a href="mysqlnd-ms.errorhandling.html">Error handling</a></div>
 <div class="next" style="text-align: right; float: right;"><a href="mysqlnd-ms.failover.html">Failover</a></div>
 <div class="up"><a href="mysqlnd-ms.concepts.html">Concepts</a></div>
 <div class="home"><a href="index.html">PHP Manual</a></div>
</div><hr /><div id="mysqlnd-ms.transient_errors" class="section">
  <h2 class="title">Transient errors</h2>
  <p class="para">
   Some distributed database clusters make use of transient errors. A transient
   error is a temporary error that is likely to disappear soon. By definition
   it is safe for a client to ignore a transient error and retry the failed
   operation on the same database server. The retry is free of side effects.
   Clients are not forced to abort their work or to fail over to another database
   server immediately. They may enter a retry loop before to wait for the
   error to disappear before giving up on the database server.
   Transient errors can be seen, for example, when using MySQL Cluster. But they
   are not bound to any specific clustering solution per se.
  </p>
  <p class="para">
   <em>PECL/mysqlnd_ms</em> can perform an automatic retry loop in
   case of a transient error. This increases distribution transparency and thus
   makes it easier to migrate an application running on a single database
   server to run on a cluster of database servers without having to change
   the source of the application.
  </p>
  <p class="para">
   The automatic retry loop will repeat the requested operation up to a user
   configurable number of times and pause between the attempts for a configurable
   amount of time. If the error disappears during the loop, the application will
   never see it. If not, the error is forwarded to the application for handling.
  </p>
  <p class="para">
   In the example below a duplicate key error is provoked to make the plugin
   retry the failing query two times before the error is passed to the application.
   Between the two attempts the plugin sleeps for 100 milliseconds.
  </p>
  <p class="para">
   <div class="example" id="example-1767">
    <p><strong>Example #1 Provoking a transient error</strong></p>
<div class="example-contents">
<div class="inicode"><pre class="inicode">mysqlnd_ms.enable=1
mysqlnd_ms.collect_statistics=1</pre>
</div>
    </div>

    <div class="example-contents">
<div class="inicode"><pre class="inicode">{
    &quot;myapp&quot;: {
        &quot;master&quot;: {
            &quot;master_0&quot;: {
                &quot;host&quot;: &quot;localhost&quot;
            }
        },
        &quot;slave&quot;: {
            &quot;slave_0&quot;: {
                &quot;host&quot;: &quot;192.168.78.136&quot;,
                &quot;port&quot;: &quot;3306&quot;
            }
       },
       &quot;transient_error&quot;: {
          &quot;mysql_error_codes&quot;: [
            1062
          ],
          &quot;max_retries&quot;: 2,
          &quot;usleep_retry&quot;: 100
       }
    }
}</pre>
</div>
    </div>

   </div>
  </p>
  <p class="para">
   <div class="example" id="example-1768">
    <p><strong>Example #2 Transient error retry loop</strong></p>
    <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000">
<span style="color: #0000BB">&lt;?php<br />$mysqli&nbsp;</span><span style="color: #007700">=&nbsp;new&nbsp;</span><span style="color: #0000BB">mysqli</span><span style="color: #007700">(</span><span style="color: #DD0000">"myapp"</span><span style="color: #007700">,&nbsp;</span><span style="color: #DD0000">"username"</span><span style="color: #007700">,&nbsp;</span><span style="color: #DD0000">"password"</span><span style="color: #007700">,&nbsp;</span><span style="color: #DD0000">"database"</span><span style="color: #007700">);<br />if&nbsp;(</span><span style="color: #0000BB">mysqli_connect_errno</span><span style="color: #007700">())<br />&nbsp;&nbsp;</span><span style="color: #FF8000">/*&nbsp;Of&nbsp;course,&nbsp;your&nbsp;error&nbsp;handling&nbsp;is&nbsp;nicer...&nbsp;*/<br />&nbsp;&nbsp;</span><span style="color: #007700">die(</span><span style="color: #0000BB">sprintf</span><span style="color: #007700">(</span><span style="color: #DD0000">"[%d]&nbsp;%s\n"</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">mysqli_connect_errno</span><span style="color: #007700">(),&nbsp;</span><span style="color: #0000BB">mysqli_connect_error</span><span style="color: #007700">()));<br /><br />if&nbsp;(!</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">query</span><span style="color: #007700">(</span><span style="color: #DD0000">"DROP&nbsp;TABLE&nbsp;IF&nbsp;EXISTS&nbsp;test"</span><span style="color: #007700">)&nbsp;||<br />&nbsp;&nbsp;&nbsp;&nbsp;!</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">query</span><span style="color: #007700">(</span><span style="color: #DD0000">"CREATE&nbsp;TABLE&nbsp;test(id&nbsp;INT&nbsp;PRIMARY&nbsp;KEY)"</span><span style="color: #007700">)&nbsp;||<br />&nbsp;&nbsp;&nbsp;&nbsp;!</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">query</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT&nbsp;INTO&nbsp;test(id)&nbsp;VALUES&nbsp;(1))"</span><span style="color: #007700">))&nbsp;{<br />&nbsp;&nbsp;</span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">"[%d]&nbsp;%s\n"</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">errno</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">error</span><span style="color: #007700">);<br />}<br /><br /></span><span style="color: #FF8000">/*&nbsp;Retry&nbsp;loop&nbsp;is&nbsp;completely&nbsp;transparent.&nbsp;Checking&nbsp;statistics&nbsp;is<br />&nbsp;the&nbsp;only&nbsp;way&nbsp;to&nbsp;know&nbsp;about&nbsp;implicit&nbsp;retries&nbsp;*/<br /></span><span style="color: #0000BB">$stats&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">mysqlnd_ms_get_stats</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">"Transient&nbsp;error&nbsp;retries&nbsp;before&nbsp;error:&nbsp;%d\n"</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$stats</span><span style="color: #007700">[</span><span style="color: #DD0000">'transient_error_retries'</span><span style="color: #007700">]);<br /><br /></span><span style="color: #FF8000">/*&nbsp;Provoking&nbsp;duplicate&nbsp;key&nbsp;error&nbsp;to&nbsp;see&nbsp;statistics&nbsp;change&nbsp;*/<br /></span><span style="color: #007700">if&nbsp;(!</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">query</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT&nbsp;INTO&nbsp;test(id)&nbsp;VALUES&nbsp;(1))"</span><span style="color: #007700">))&nbsp;{<br />&nbsp;&nbsp;</span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">"[%d]&nbsp;%s\n"</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">errno</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">error</span><span style="color: #007700">);<br />}<br /><br /></span><span style="color: #0000BB">$stats&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">mysqlnd_ms_get_stats</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">printf</span><span style="color: #007700">(</span><span style="color: #DD0000">"Transient&nbsp;error&nbsp;retries&nbsp;after&nbsp;error:&nbsp;%d\n"</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$stats</span><span style="color: #007700">[</span><span style="color: #DD0000">'transient_error_retries'</span><span style="color: #007700">]);<br /><br /></span><span style="color: #0000BB">$mysqli</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">close</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span>
</span>
</code></div>
    </div>

<div class="example-contents"><p>以上例程的输出类似于：</p></div>
    <div class="example-contents screen">
<div class="cdata"><pre>
Transient error retries before error: 0
[1062] Duplicate entry &#039;1&#039; for key &#039;PRIMARY&#039;
Transient error retries before error: 2
</pre></div>
    </div>
   </div>
  </p>
  <p class="para">
   Because the execution of the retry loop is transparent from a users point of
   view, the example checks the
   <a href="function.mysqlnd-ms-get-stats.html" class="link">statistics</a>
   provided by the plugin to learn about it.
  </p>
  <p class="para">
   As the example shows, the plugin can be instructed to consider any error
   transient regardless of the database servers error semantics. The only error
   that a stock MySQL server considers temporary has the error code
   <strong><code>1297</code></strong>. When configuring other error codes but
   <strong><code>1297</code></strong> make sure your configuration reflects
   the semantics of your clusters error codes.
  </p>
  <p class="para">
   The following mysqlnd C API calls are monitored by the plugin to check
   for transient errors: <em>query()</em>,
   <em>change_user()</em>, <em>select_db()</em>,
   <em>set_charset()</em>, <em>set_server_option()</em>
   <em>prepare()</em>, <em>execute()</em>,
   <em>set_autocommit()</em>,
   <em>tx_begin()</em>, <em>tx_commit()</em>,
   <em>tx_rollback()</em>, <em>tx_commit_or_rollback()</em>.
   The corresponding user API calls have similar names.
  </p>
  <p class="para">
   The maximum time the plugin may sleep during the retry loop depends on the
   function in question. The a retry loop for <em>query()</em>,
   <em>prepare()</em> or <em>execute()</em> will sleep for
   up to <em>max_retries * usleep_retry</em> milliseconds.
  </p>
  <p class="para">
   However, functions that
   <a href="mysqlnd-ms.pooling.html" class="link">control connection state</a>
   are dispatched to all all connections. The retry loop settings are applied
   to every  connection on which the command is to be run. Thus, such a function
   may interrupt program execution for longer than a function that is run
   on one server only. For example, <em>set_autocommit()</em> is
   dispatched to connections and may sleep up to
   <em>(max_retries * usleep_retry) * number_of_open_connections)</em>
   milliseconds. Please, keep this in mind when setting long sleep times
   and large retry numbers. Using the default settings of
   <em>max_retries=1</em>, <em>usleep_retry=100</em> and
   <em>lazy_connections=1</em> it is unlikely that you will
   ever see a delay of more than 1 second.
  </p>
 </div><hr /><div class="manualnavbar" style="text-align: center;">
 <div class="prev" style="text-align: left; float: left;"><a href="mysqlnd-ms.errorhandling.html">Error handling</a></div>
 <div class="next" style="text-align: right; float: right;"><a href="mysqlnd-ms.failover.html">Failover</a></div>
 <div class="up"><a href="mysqlnd-ms.concepts.html">Concepts</a></div>
 <div class="home"><a href="index.html">PHP Manual</a></div>
</div></body></html>
